package de.jmda.util.gui.awt.graphics;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;

public class RelationEndpoint extends Polygon
{
  private static final long serialVersionUID = 1L;

	public enum Orientation
	{
		LEFT,
		RIGHT,
		UP,
		DOWN;
		
		public boolean isHorizontal()
		{
			return (this == LEFT) || (this == RIGHT);
		}
		
		public boolean isVertical()
		{
			return !isHorizontal();
		}
	}

	public enum Style
	{
		FILLED,
		TRANSPARENT,
		OPEN,
		NONE
	}

	private Point headLocation;
	private int length;
	private Style style;
	private boolean dashed;
	private int headSize;
	private Orientation orientation;

	public RelationEndpoint(
			Point headLocation,
			int length,
			Style style,
			boolean dashed,
			int headSize,
			Orientation orientation)
	{
		this.headLocation = headLocation;
		this.length = length;
		this.style = style;
		this.dashed = dashed;
		this.headSize = headSize;
		this.orientation = orientation;
		
		computePolygon();
	}

  public void paint(Graphics2D graphics2D)
  {
		if (style == Style.FILLED)
		{
			graphics2D.fillPolygon(this);
		}
		
		if (dashed)
		{
			// save old stroke to be able to restore stroke
			Stroke oldStroke = graphics2D.getStroke();

			// x pixel line, x pixel gap, x pixel line, x pixel gap
			float[] dashPattern = { 5, 5, 5, 5};
			
			Stroke newStroke =
					new BasicStroke(
							10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, dashPattern, 0);
			
			graphics2D.setStroke(newStroke);
			graphics2D.drawPolygon(this);
			graphics2D.setStroke(oldStroke);
		}
		else
		{
			graphics2D.drawPolygon(this);
		}
  }
	
	/**
	 * Virtually extend the real bounds by <code>2</code> in horizontal direction
	 * for instances that have <code>{@link #isVertical()} == true</code> and in
	 * vertical direction for instances that have <code>{@link #isHorizontal()} ==
	 * true</code>.
	 *
	 * @see java.awt.Polygon#contains(int, int)
	 */
	@Override
	public boolean contains(int x, int y)
	{
		Rectangle2D rectangle = getBounds2D();
		Rectangle2D virtualRectangle;

		if (isHorizontal())
		{
			virtualRectangle =
					new Rectangle2D.Double(
							rectangle.getX()    , rectangle.getY()      - 2,
							rectangle.getWidth(), rectangle.getHeight() + 2);
			return virtualRectangle.contains(x, y);
		}

		virtualRectangle =
				new Rectangle2D.Double(
						rectangle.getX()     - 2, rectangle.getY(),
						rectangle.getWidth() + 4, rectangle.getHeight());
//							4                   , rectangle.getHeight());
		return virtualRectangle.contains(x, y);
	}
	
	public boolean isHorizontal()
	{
		return
				((orientation == Orientation.LEFT) ||
				 (orientation == Orientation.RIGHT));
	}
	
	public boolean isVertical()
	{
		return isHorizontal() == false;
	}

	/**
   * @return the headLocation
   */
  public Point getHeadLocation()
  {
  	return headLocation;
  }

	/**
   * @return the length
   */
  public int getLength()
  {
  	return length;
  }

	/**
   * @return the style
   */
  public Style getStyle()
  {
  	return style;
  }

	/**
   * @return the dashed
   */
  public boolean isDashed()
  {
  	return dashed;
  }

	/**
   * @return the headSize
   */
  public int getHeadSize()
  {
  	return headSize;
  }

	/**
   * @return the orientation
   */
  public Orientation getOrientation()
  {
  	return orientation;
  }

	/**
   * @param relationEndpointLocation the relationEndpointLocation to set
   */
  public void setHeadLocation(Point relationEndpointLocation)
  {
  	headLocation = relationEndpointLocation;
  	computePolygon();
  }

	/**
   * @param relationEndpointLength the relationEndpointLength to set
   */
  public void setLength(int relationEndpointLength)
  {
  	length = relationEndpointLength;
  	computePolygon();
  }

	/**
   * @param style the style to set
   */
  public void setStyle(Style style)
  {
  	this.style = style;
  	computePolygon();
  }

	/**
   * @param dashed the dashed to set
   */
  public void setDashed(boolean dashed)
  {
  	this.dashed = dashed;
  	computePolygon();
  }

	/**
   * @param arrowHeadSize the arrowHeadSize to set
   */
  public void setHeadSize(int arrowHeadSize)
  {
  	headSize = arrowHeadSize;
  	computePolygon();
  }

	/**
   * @param orientation the orientation to set
   */
  public void setOrientation(Orientation orientation)
  {
  	this.orientation = orientation;
  	computePolygon();
  }
  
  private void computePolygon()
  {
  	reset();
  	
		int x = headLocation.x;
		int y = headLocation.y;
		
		if (orientation == Orientation.DOWN)
		{
			if (style == Style.FILLED)
			{
				addPoint(x, y);
				addPoint(x - headSize, y - headSize);
				addPoint(x + headSize, y - headSize);
				addPoint(x, y);
				addPoint(x, y - length);
			}
			else if (style == Style.TRANSPARENT)
			{
				addPoint(x, y - headSize);
				addPoint(x - headSize, y - headSize);
				addPoint(x, y);
				addPoint(x + headSize, y - headSize);
				addPoint(x, y - headSize);
				addPoint(x, y - length);
			}
			else if (style == Style.OPEN)
			{
				addPoint(x, y);
				addPoint(x - headSize, y - headSize);
				addPoint(x, y);
				addPoint(x + headSize, y - headSize);
				addPoint(x, y);
				addPoint(x, y - length);
			}
			else if (style == Style.NONE)
			{
				addPoint(x, y);
				addPoint(x, y - length);
			}
		}
		else if (orientation == Orientation.UP)
		{
			if (style == Style.FILLED)
			{
				addPoint(x, y);
				addPoint(x - headSize, y + headSize);
				addPoint(x + headSize, y + headSize);
				addPoint(x, y);
				addPoint(x, y + length);
			}
			else if (style == Style.TRANSPARENT)
			{
				addPoint(x, y + headSize);
				addPoint(x - headSize, y + headSize);
				addPoint(x, y);
				addPoint(x + headSize, y + headSize);
				addPoint(x, y + headSize);
				addPoint(x, y + length);
			}
			else if (style == Style.OPEN)
			{
				addPoint(x, y);
				addPoint(x - headSize, y + headSize);
				addPoint(x, y);
				addPoint(x + headSize, y + headSize);
				addPoint(x, y);
				addPoint(x, y + length);
			}
			else if (style == Style.NONE)
			{
				addPoint(x, y);
				addPoint(x, y + length);
			}
		}
		else if (orientation == Orientation.LEFT)
		{
			if (style == Style.FILLED)
			{
				addPoint(x, y);
				addPoint(x + headSize, y - headSize);
				addPoint(x + headSize, y + headSize);
				addPoint(x, y);
				addPoint(x + length, y);
			}
			else if (style == Style.TRANSPARENT)
			{
				addPoint(x + headSize, y);
				addPoint(x + headSize, y - headSize);
				addPoint(x, y);
				addPoint(x + headSize, y + headSize);
				addPoint(x + headSize, y);
				addPoint(x + length, y);
			}
			else if (style == Style.OPEN)
			{
				addPoint(x, y);
				addPoint(x + headSize, y - headSize);
				addPoint(x, y);
				addPoint(x + headSize, y + headSize);
				addPoint(x, y);
				addPoint(x + length, y);
			}
			else if (style == Style.NONE)
			{
				addPoint(x, y);
				addPoint(x + length, y);
			}
		}
		else if (orientation == Orientation.RIGHT)
		{
			if (style == Style.FILLED)
			{
				addPoint(x, y);
				addPoint(x - headSize, y - headSize);
				addPoint(x - headSize, y + headSize);
				addPoint(x, y);
				addPoint(x - length, y);
			}
			else if (style == Style.TRANSPARENT)
			{
				addPoint(x - headSize, y);
				addPoint(x - headSize, y - headSize);
				addPoint(x, y);
				addPoint(x - headSize, y + headSize);
				addPoint(x - headSize, y);
				addPoint(x - length, y);
			}
			else if (style == Style.OPEN)
			{
				addPoint(x, y);
				addPoint(x - headSize, y - headSize);
				addPoint(x, y);
				addPoint(x - headSize, y + headSize);
				addPoint(x, y);
				addPoint(x - length, y);
			}
			else if (style == Style.NONE)
			{
				addPoint(x, y);
				addPoint(x - length, y);
			}
		}
  }
}